/*
 * Copyright Konstantin Triger <kostat@gmail.com> 
 * 
 * This file is part of Jaque 2 JPA - JAva QUEry 2 JPA library <http://code.google.com/p/jaque/>.
 * 
 * Jaque is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Jaque is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package jaque.jpa;

import static org.junit.Assert.*;
import static jaque.DynamicOperation.*;
import static jaque.DynamicQuery.*;

import java.sql.*;

import jaque.Operation;
import jaque.functions.*;

import javax.persistence.*;

import org.junit.*;

public class Northwind {
	private static EntityManagerFactory _emf;

	private EntityManager _em;
	private JaqueEntityManager _jem;

	@BeforeClass
	public static void beforeClass() throws SQLException {
		DerbyUtils.BeforeClass();
		_emf = Persistence.createEntityManagerFactory("hibernate"); // openjpa
		// hibernate
	}

	@AfterClass
	public static void afterClass() {

		if (_emf != null)
			_emf.close();
		DerbyUtils.AfterClass();
	}

	@Before
	public void before() {
		_em = _emf.createEntityManager();
		_jem = new JaqueEntityManager(_em);
	}

	public void after() {
		_em.close();
	}

	@Test
	//@Ignore("OpenJPA fails due to http://issues.apache.org/jira/browse/OPENJPA-26")
	public void testIntNegate() {
		// TestQuery(db.Orders.Where(o => -o.OrderID == -1));

		assertEquals(0, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return -t.getOrderID() == -1;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	@Ignore("JPA does not support unary ~ operator")
	public void testIntBitwiseNot() {
		// TestQuery(db.Orders.Where(o => ~o.OrderID == 0));

		assertEquals(0, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return ~t.getOrderID() == 0;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntLessThan() {
		// TestQuery(db.Orders.Where(o => o.OrderID < 0));

		assertEquals(0, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() < 0;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntLessThan1() {
		// TestQuery(db.Orders.Where(o => o.OrderID < 0));

		assertEquals(0, Operation.count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() < 0;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntLessThanOrEqual() {
		// TestQuery(db.Orders.Where(o => o.OrderID <= 0));

		assertEquals(0, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() <= 0;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntGreaterThan() {
		// TestQuery(db.Orders.Where(o => o.OrderID > 0));

		assertEquals(830, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() > 0;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntGreaterThanOrEqual() {
		// TestQuery(db.Orders.Where(o => o.OrderID >= 0));

		assertEquals(830, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() >= 0;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntAdd() {
		// TestQuery(db.Orders.Where(o => o.OrderID + 0 == 0));

		assertEquals(830, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() + 0 == t.getOrderID();
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntSubtract() {
		// TestQuery(db.Orders.Where(o => o.OrderID - 0 == 0));

		assertEquals(830, Operation.count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() - 0 == t.getOrderID();
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntMultiply() {
		// TestQuery(db.Orders.Where(o => o.OrderID * 1 == 1));

		assertEquals(830, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() * 1 == t.getOrderID();
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntDivide() {
		// TestQuery(db.Orders.Where(o => o.OrderID / 1 == 1));

		assertEquals(830, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() / 1 == t.getOrderID();
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testIntModulo() {
		// TestQuery(db.Orders.Where(o => o.OrderID % 1 == 0));

		assertEquals(830, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderID() % 1 == 0;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testMathAbs() {
		// TestQuery(db.Orders.Where(o => Math.Abs(o.OrderID) == 10));

		assertEquals(697, Operation.count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return Math.abs(t.getOrderID()) > 10380;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testMathSqrt() {
		// TestQuery(db.Orders.Where(o => Math.Sqrt(o.OrderID) == 0));

		assertEquals(0, count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return Math.sqrt(t.getOrderID()) == 0;
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testDateTimeCurrentDate() {
		// TestQuery(db.Orders.Where(o => Math.Sqrt(o.OrderID) == 0));
		assertEquals(0, Operation.count(where(new Predicate<Order>() {
			public Boolean invoke(Order t) throws Throwable {
				return t.getOrderDate().before(
						new java.sql.Date(System.currentTimeMillis()));
			}
		}, _jem.from(Order.class))));
	}

	@Test
	public void testSize() {

		assertEquals(55, count(where(new Predicate<Customer>() {
			public Boolean invoke(Customer t) throws Throwable {
				return t.getOrders().size() > 6;
			}
		}, _jem.from(Customer.class))));
	}

	@Test
	public void testStringLength() {

		assertEquals(46, Operation.count(where(new Predicate<Customer>() {
			public Boolean invoke(Customer t) throws Throwable {
				return t.getCity().length() > 7;
			}
		}, _jem.from(Customer.class))));

		// assertEquals(46, count(from(_jem.entity(Customer.class), where(
		// new Predicate<Customer>() {
		// public Boolean invoke(Customer t) throws Throwable {
		// return t.getCity().length() > 7;
		// }
		// }, select(new Function<Customer, Customer>() {
		// public Customer invoke(Customer t) throws Throwable {
		// return t;
		// }
		// })))));
	}

	@Test
	public void testStringSubstring() {

		final int x = 1;
		final int y = 0;

		// for (String s : from(_jem.entity(Customer.class), where(
		// new Predicate<Customer>() {
		// public Boolean invoke(Customer t) throws Throwable {
		// return t.getCity().substring(y + x, 3) == "Lon";
		// }
		// }, select(new Function<String, Customer>() {
		// public String invoke(Customer t) throws Throwable {
		// return t.getCity();
		// }
		// })))) {
		// System.out.println(s);
		// }

		assertEquals(6, count(select(new Function<String, Customer>() {
			public String invoke(Customer t) throws Throwable {
				return t.getCity();
			}
		}, where(new Predicate<Customer>() {
			public Boolean invoke(Customer t) throws Throwable {
				return t.getCity().substring(1, 3) == "Lon";
			}
		}, _jem.from(Customer.class)))));

		// assertEquals(6, count(from(_jem.entity(Customer.class), where(
		// new Predicate<Customer>() {
		// public Boolean invoke(Customer t) throws Throwable {
		// return t.getCity().substring(1, 3) == "Lon";
		// }
		// }, select(new Function<String, Customer>() {
		// public String invoke(Customer t) throws Throwable {
		// return t.getCity();
		// }
		// })))));

	}

	@Test
	public void testNew() {

		assertEquals(10, Operation.count(new Predicate<Foo>() {
			public Boolean invoke(Foo t) throws Throwable {
				return t.getS().length() < 6;
			}
		}, select(new Function<Foo, Customer>() {
			public Foo invoke(Customer t) throws Throwable {
				return new Foo(t.getCity());
			}
		}, where(new Predicate<Customer>() {
			public Boolean invoke(Customer t) throws Throwable {
				return t.getOrders().size() > 6;
			}
		}, _jem.from(Customer.class)))));
	}
}
